﻿using System;
using System.Collections.Generic;
using SR = System.Reflection;
using Mono.Cecil;

namespace AgregarTrace
{
    class TracerReferences : Dictionary<string, MethodReference>
    {
        public TracerReferences (
            string pathTracer,
            AssemblyDefinition targetAssembly
        )
        {
            SR.MethodInfo   mi      = null;
            SR.Assembly     assembly= SR.Assembly.LoadFrom(pathTracer);
            Type            type    = assembly.GetType("ParasiTracer.Tracer");

            // traces
            mi = type.GetMethod("inFunc", new Type[] { typeof(int) });
            Add("inFunc", targetAssembly.importMethodInfo(mi));

            mi = type.GetMethod("inFuncWithParams", new Type[] { typeof(int) });
            Add("inFuncWithParams", targetAssembly.importMethodInfo(mi));

            mi = type.GetMethod("outFunc", new Type[] { typeof(int) });
            Add("outFunc", targetAssembly.importMethodInfo(mi));

            mi = type.GetMethod("throwFunc", new Type[] { typeof(int) });
            Add("throwFunc", targetAssembly.importMethodInfo(mi));

            // params
            string[] funcsParams = {
                "booleanParam" , 
                "charParam" , 
                "sbyteParam" , 
                "byteParam" , 
                "int16Param" , 
                "uint16Param" , 
                "int32Param" , 
                "uint32Param" , 
                "int64Param" , 
                "uint64Param" , 
                "singleParam" , 
                "doubleParam" , 
                "stringParam" , 
                "intptrParam" , 
                "uintptrParam" , 
                "objectParam"
            };

            foreach (string sFuncParam in funcsParams) {
                mi = type.GetMethod(sFuncParam);
                if (mi == null)
                    throw new Exception("El metodo " + sFuncParam + " no fue encontrado en la dll");
                Add(sFuncParam, targetAssembly.importMethodInfo(mi));
            }
        }

        public MethodReference this[MetadataType mt]
        {
            get {
                switch (mt) {
                    case MetadataType.Boolean : return this["booleanParam"];
                    case MetadataType.Char    : return this["charParam"   ];
                    case MetadataType.SByte   : return this["sbyteParam"  ];
                    case MetadataType.Byte    : return this["byteParam"   ];
                    case MetadataType.Int16   : return this["int16Param"  ];
                    case MetadataType.UInt16  : return this["uint16Param" ];
                    case MetadataType.Int32   : return this["int32Param"  ];
                    case MetadataType.UInt32  : return this["uint32Param" ];
                    case MetadataType.Int64   : return this["int64Param"  ];
                    case MetadataType.UInt64  : return this["uint64Param" ];
                    case MetadataType.Single  : return this["singleParam" ];
                    case MetadataType.Double  : return this["doubleParam" ];
                    case MetadataType.String  : return this["stringParam" ];
                    case MetadataType.Object  : return this["objectParam" ];
                    case MetadataType.Class   : return this["objectParam" ];
                    case MetadataType.Array   : return this["objectParam" ];
                    case MetadataType.GenericInstance : return this["objectParam"];
                    case MetadataType.ValueType : return this["objectParam"]; // <- (box-ear)
                    default: throw new Exception("No hay método para este MetadataType!");
                }
            }
        }
    }
}
